from Tkinter import BOTH,NONE,X,Y,BOTTOM,TOP,LEFT,RIGHT
from djcontrolutil import H,V

#Should also provide a good way to bind events.
#Should expose methods common to all widgets

class DjControl():
	_basecontrol = None

	def __getattr__(self, strAttribute):
		if strAttribute=='basecontrol': return self._basecontrol
		return self._getorset(False, strAttribute)
		
	def __setattr__(self, strAttribute, value):
		return self._getorset(True, strAttribute, value)
	
	def destroy(self): #Remove
		self.basecontrol.destroy()
	
	def tk_pack(self,*args, **options):
		""" Place on form using tkinter pack() method. Prefer add_to_window."""
		self.basecontrol.pack(**options)
		return self
	def grid(self,*args, **options):
		""" Place on form using grid() method."""
		self.basecontrol.grid(**options)
		return self
	def place(self,*args, **options):
		""" Place on form using place() method."""
		self.basecontrol.place(**options)
		return self
	
	
	def add_to_window(self, pack_orientation=H, fill=False, auto_size=False, pad=None, anchor=None):
		'''A wrapper around pack(). fill is one of True, False, 'x', or 'y'. auto_size is True or False. Pad is either an integer (padding) or a tuple for horizontal padding and vertical padding.'''
		kwds={}
		
		# where to add the widget. To the right of previously added ones, or to the bottom.
		if pack_orientation==V: kwds['side']=TOP
		else: kwds['side']=LEFT
		
		if fill == True: kwds['fill'] = BOTH
		elif fill == 'x': kwds['fill'] = X
		elif fill == 'y': kwds['fill'] = Y
		
		if auto_size: kwds['expand'] = True
		
		if pad!=None:
			if isinstance(pad,tuple) or isinstance(pad,list):
				kwds['padx']=pad[0]
				kwds['pady']=pad[1]
			else:
				kwds['padx']=pad
				kwds['pady']=pad
				
		self.tk_pack(**kwds)
		return self
		
	
	def tkmethod(self, strMethod, *args, **kwargs):
		""" Call an arbitrary method on the underlying tkinter control. If you need to, you can access the "basecontrol" property as well. """
		if strMethod not in self.basecontrol.__dict__:
			raise RuntimeError, 'Tkinter method not found. Check the tkinter documentation.'
		return self.basecontrol.__dict__[strMethod](*args, **kwargs)
		
	def set_many(self,**options): #take note that these are dj, not tk options.
		"""Set many properties of the control at once."""
		# It is inefficient to do this instead of config which could set many properties at once, but I'm not worried about this for now
		for option in options:
			self._getorset(True, option, options[option])
	
	def _getorset(self, bSet, strAttribute, value=None):
		#Properties - can perform an action when set or read.
		#I choose not to use Python's built in properties, although they are nice, because I want to make "obj.nonExistantProperty = 3" create a runtime error.
		
		# Many properties are a simple wrapper of a Tkinter setting. 
		# _autoProperties is a dict
		if self._autoProperties!=None and strAttribute in self._autoProperties:
			strImplProp = self._autoProperties[strAttribute][0]
			fnSetWrapper = self._autoProperties[strAttribute][1]
			fnGetWrapper = self._autoProperties[strAttribute][2]
			if bSet:
				if fnSetWrapper==None: self.basecontrol[strImplProp] = value
				else: self.basecontrol[strImplProp] = fnSetWrapper(value)
				self.basecontrol.update_idletasks()
			else: 
				if fnGetWrapper==None: return self.basecontrol[strImplProp]
				else: return fnGetWrapper(self.basecontrol[strImplProp])
					
		elif strAttribute in self._customProperties:
			# Some have manual properties that are more complicated than just a conversion.
			return self._lookupCustomProperties(bSet, strAttribute, value)
		else:
			# Asymmetry between get and set. The override ALWAYS occurs for set, but for get, it checks existing members first.
			if bSet:
				# Set as usual
				if strAttribute[0]=='_' or strAttribute in ['basecontrol']:
					#~ print strAttribute, 'value:', value, 'o'
					self.__dict__[strAttribute] = value #~ object.__setattr__(self, strAttribute, value)
				else:
					raise AttributeError, 'You are trying to set "%s", which is not a valid property.'% strAttribute
			else:
				# This means that the usual method already failed.
				raise AttributeError, 'You are trying to set "%s", which is not a valid property.'% strAttribute
	
	# Events
	def bind(self, *args):
		self.basecontrol.bind(*args)
	
	# Base methods and properties.
	def focus(self):
		self.basecontrol.focus_set()
	def has_focus(self):
		return self.basecontrol.focus_get()
	
